#include <stdio.h>

#include "freertos.h"
#include "task.h"

#include "bms_global.h"
#include "bms_protect.h"
#include "bms_monitor.h"
#include "bms_hal_control.h"

// thread config
#define PROTECT_TASK_STACK_SIZE	128
#define PROTECT_TASK_PRIORITY	  3
#define PROTECT_TASK_PERIOD		  200

static TaskHandle_t ProtectTaskHandler = NULL;

static void BMS_ProtectTaskEntry( void *paramter );
static void BMS_ProtectTriggerMonitor( void );
static void BMS_ProtectRelieveMonitor( void );
static void BMS_DischargeMonitor( void );

static void BMS_ChargeMonitor( void );

BMS_ProtectTypedef BMS_Protect = 
{
	.alert = FLAG_ALERT_NO,
	.param = 
	{
		.ShoutdownVoltage = INIT_SHUTDOWN_VOLTAGE,

		.OVProtect	= INIT_OV_PROTECT,
		.OVRelieve	= INIT_OV_RELIEVE,
		.UVProtect	= INIT_UV_PROTECT,
		.UVRelieve	= INIT_UV_RELIEVE,

		.OCCProtect = INIT_OCC_MAX,
		.OCDProtect = INIT_OCD_MAX,

		.OVDelay	= INIT_OV_DELAY,
		.UVDelay	= INIT_UV_DELAY,
		.OCDDelay	= INIT_OCD_DELAY,
		.SCDDelay	= INIT_SCD_DELAY,

		.OCDRelieve = INIT_OCD_RELIEVE,
		.SCDRelieve = INIT_SCD_RELIEVE,
		.OCCDelay	= INIT_OCC_DELAY,
		.OCCRelieve = INIT_OCC_RELIEVE,

		.OTCProtect = INIT_OTC_PROTECT,
		.OTCRelieve = INIT_OTC_RELIEVE,
		.OTDProtect = INIT_OTD_PROTECT,
		.OTDRelieve = INIT_OTD_RELIEVE,

		.LTCProtect = INIT_LTC_PROTECT,
		.LTCRelieve = INIT_LTC_RELIEVE,
		.LTDProtect = INIT_LTD_PROTECT,
		.LTDRelieve = INIT_LTD_RELIEVE,
	}
};


void BMS_ProtectInit(void)
{
	BaseType_t res = xTaskCreate(BMS_ProtectTaskEntry,
															 "protect",
	                             PROTECT_TASK_STACK_SIZE,
	                             NULL,
															 PROTECT_TASK_PRIORITY,
															 &ProtectTaskHandler);
	if(res != pdPASS)
	{
		printf("Create Protect Task Fail\r\n");
	}
}

void BMS_ProtectTaskEntry(void *paramter)
{	
	while(1)
	{
		BMS_ProtectTriggerMonitor();
		BMS_ProtectRelieveMonitor();
		vTaskDelay(pdMS_TO_TICKS(PROTECT_TASK_PERIOD));
	}
}

// Protection trigger monitoring, Software-triggered.
static void BMS_ProtectTriggerMonitor( void )
{
	switch( BMS_GlobalParam.SysMode )
	{
		case BMS_MODE_CHARGE:
		{
			BMS_ChargeMonitor();
		}break;
		
		case BMS_MODE_DISCHARGE:
		{
			BMS_DischargeMonitor();
		}break;
	
		default: break;
	}
}

static void BMS_ProtectRelieveMonitor( void )
{
	static uint32_t RelieveCountCHG = 0, RelieveCountDSG = 0;
	
	if( BMS_Protect.alert != FLAG_ALERT_NO)
	{
		// over voltage recover
		if( BMS_Protect.alert & FLAG_ALERT_OV )
		{
			if( BMS_MonitorData.CellData[ BMS_CELL_MAX - 1 ].CellVoltage < BMS_Protect.param.OVRelieve )
			{
				BMS_Protect.alert &= ~FLAG_ALERT_OV;
				printf("Charge: Ov Relieve\r\n");
			}
		}
		
		// over temperature in charge mode
		else if( BMS_Protect.alert & FLAG_ALERT_OTC && BMS_MonitorData.CellTempEffectiveNumber != 0 )
		{
			if( BMS_MonitorData.CellTemp[BMS_TEMP_MAX - 1] <  BMS_Protect.param.OTCRelieve )
			{
				BMS_Protect.alert &= ~FLAG_ALERT_OTC;
				printf("Charge: OTC Relieve \r\n");
			}
		}
		
		// under temperature in charge mode
		else if( BMS_Protect.alert & FLAG_ALERT_LTC && BMS_MonitorData.CellTempEffectiveNumber != 0 )
		{
			if( BMS_MonitorData.CellTemp[0] > BMS_Protect.param.LTCRelieve )
			{
				BMS_Protect.alert &= ~FLAG_ALERT_LTC;
				printf("Charge: LTC Relieve \r\n");
			}
		}
		
		// over current in charge mode
		else if( BMS_Protect.alert & FLAG_ALERT_OCC )
		{
			RelieveCountCHG +=  PROTECT_TASK_PERIOD;
			if( RelieveCountCHG / 1000 > BMS_Protect.param.OCCDelay )
			{
				RelieveCountCHG = 0;
				BMS_Protect.alert &= ~FLAG_ALERT_OCC;
				printf("Charge: OCC Relieve \r\n");
			}
		}
		
		// under voltage in discharge mode
		if( BMS_Protect.alert & FLAG_ALERT_UV )
		{
			if( BMS_MonitorData.CellData[0].CellVoltage > BMS_Protect.param.UVRelieve )
			{
				BMS_Protect.alert &= ~FLAG_ALERT_UV;
				printf("Discharge:UV Relieve");
			}
		}
		
		// over temperature in discharge mode
		else if (BMS_Protect.alert & FLAG_ALERT_OTD && BMS_MonitorData.CellTempEffectiveNumber != 0)
		{
			if (BMS_MonitorData.CellTemp[BMS_TEMP_MAX-1] < BMS_Protect.param.OTDRelieve)
			{
				BMS_Protect.alert &= ~FLAG_ALERT_OTD;
				BMS_HalCtrlDischarge(BMS_GlobalParam.Discharge);
				printf("Discharge:OTD Relieve");
			}
		}
		
		// under temperature in discharge mode
		else if (BMS_Protect.alert & FLAG_ALERT_LTD && BMS_MonitorData.CellTempEffectiveNumber != 0)
		{
			if (BMS_MonitorData.CellTemp[0] > BMS_Protect.param.LTDRelieve)
			{
				BMS_Protect.alert &= ~FLAG_ALERT_LTD;
				BMS_HalCtrlDischarge(BMS_GlobalParam.Discharge);
				printf("Discharge:LTD Relieve");
			}
		}
		
		// over current in dischare mode
		else if (BMS_Protect.alert & FLAG_ALERT_OCD)
		{
			RelieveCountDSG += PROTECT_TASK_PERIOD;
			if (RelieveCountDSG / 60 >= BMS_Protect.param.OCDRelieve)
			{
				RelieveCountDSG = 0;

				BMS_Protect.alert &= ~FLAG_ALERT_OCD;
				BMS_HalCtrlDischarge(BMS_GlobalParam.Discharge);
				printf("Discharge:OCD Relieve");
			}
		}
		
		// short circurt  
		else if (BMS_Protect.alert & FLAG_ALERT_SCD)
		{
			RelieveCountDSG += PROTECT_TASK_PERIOD;
			if (RelieveCountDSG / 60 >= BMS_Protect.param.SCDRelieve)
			{
				RelieveCountDSG = 0;

				BMS_Protect.alert &= ~FLAG_ALERT_SCD;		
				BMS_HalCtrlDischarge(BMS_GlobalParam.Discharge);
				printf("Discharge:SCD Relieve");
			}
		}
	}
	
	
}

/**
  * @brief  Charging monitoring: overcurrent, overtemperature, low temperature.
	*         When any one of the three protections is triggered, charging will be halted 
  *         until the recovery conditions are met.
  */
static void BMS_ChargeMonitor( void )
{
	static uint32_t ProtectCount = 0;
	
	// check over current in charge mode.
	if( BMS_MonitorData.BatteryCurrent > BMS_Protect.param.OCCProtect )
	{
		ProtectCount += PROTECT_TASK_PERIOD;
		if(ProtectCount >= BMS_Protect.param.OCCDelay * 1000 / PROTECT_TASK_PERIOD)
		{
			BMS_HalCtrlCharge(BMS_STATE_DISABLE);
			BMS_Protect.alert = FLAG_ALERT_OCC;
			printf("Charge:OCC Protect Trigger\r\n");
		}
	}
	else
	{
		ProtectCount = 0;
	}	
	
	if( BMS_MonitorData.CellTempEffectiveNumber == 0 )
	{
		return;
	}
	
	// Low temperature
	else if( BMS_MonitorData.CellTemp[0] < BMS_Protect.param.LTCProtect )
	{
		BMS_HalCtrlCharge( BMS_STATE_DISABLE );
		BMS_Protect.alert = FLAG_ALERT_LTC;
		printf("Charge: LTC Protect Trigger\r\n");
	}
	
	// Over temperature
	else if( BMS_MonitorData.CellTemp[BMS_MonitorData.CellTempEffectiveNumber - 1] > BMS_Protect.param.OTCProtect )
	{
		BMS_HalCtrlCharge( BMS_STATE_DISABLE );
		BMS_Protect.alert = FLAG_ALERT_OTC;
		printf("Charge: OTC Protect Trigger\r\n");
	}
}

/**
  * @brief  Discharging monitoring: overcurrent, overtemperature, low temperature.
	*         When any one of the three protections is triggered, charging will be halted 
  *         until the recovery conditions are met.
  */
static void BMS_DischargeMonitor( void )
{
	if( BMS_MonitorData.CellTempEffectiveNumber == 0 )
	{
		return;
	}
	
	else if( BMS_MonitorData.CellTemp[BMS_MonitorData.CellTempEffectiveNumber - 1] >  BMS_Protect.param.OTDProtect )
	{
		BMS_HalCtrlDischarge( BMS_STATE_DISABLE );
		BMS_Protect.alert = FLAG_ALERT_OTD;
		printf("Discharge: OTD protect trigger\r\n");
	}
	else if( BMS_MonitorData.CellTemp[0] < BMS_Protect.param.LTDProtect )
	{
		BMS_HalCtrlDischarge( BMS_STATE_DISABLE );
		BMS_Protect.alert = FLAG_ALERT_LTD;
		printf("Discharge: LTD protect trigger\r\n");
	}
}

/**
  * @brief  Hardware detection of overcurrent during charging.
	*
  */
void BMS_ProtectHwOCD(void)
{
	if( ( BMS_Protect.alert & FLAG_ALERT_OCD ) ==  FLAG_ALERT_NO )
	{
		BMS_HalCtrlDischarge( BMS_STATE_DISABLE );
		BMS_Protect.alert |= FLAG_ALERT_OCD;
		printf("Discharge: OCD protect trigger\r\n");
	}
}

/**
  * @brief  Hardware detection of short circut during charging.
	*
  */
void BMS_ProtectHwSCD(void)
{
	if( ( BMS_Protect.alert & FLAG_ALERT_SCD ) == FLAG_ALERT_NO )
	{
		BMS_HalCtrlDischarge( BMS_STATE_DISABLE );
		BMS_Protect.alert |= FLAG_ALERT_SCD;
		printf("Discharge: SCD protect trigger\r\n");
	}
}

/**
  * @brief  Hardware detection of over voltage during charging.
	*
  */
void BMS_ProtectHwOV(void)
{
	if( ( BMS_Protect.alert & FLAG_ALERT_OV ) == FLAG_ALERT_NO )
	{
		BMS_HalCtrlDischarge( BMS_STATE_DISABLE );
		BMS_Protect.alert |= FLAG_ALERT_OV;
		printf("Discharge: OV protect trigger\r\n");
	}
}

/**
  * @brief Hardware detection of under voltage during discharging.
	*
  */
void BMS_ProtectHwUV( void )
{
	if( ( BMS_Protect.alert & FLAG_ALERT_UV ) == FLAG_ALERT_NO )
	{
		BMS_HalCtrlDischarge( BMS_STATE_DISABLE );
		BMS_Protect.alert |= FLAG_ALERT_UV;
		printf("Discharge: UV protect trigger\r\n");
	}
}

/**
  * @brief Equipment malfunction alarm.
  *
	* @note Additional processing code can be added later. For now, only outputting debug information here. 
  */
void BMS_ProtectHwDevice( void )
{
	printf("BMS_ProtectHwDevice\r\n");
}
